home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / cvs-1_3.lha / cvs-1.3 / src / diff.c < prev    next >
C/C++ Source or Header  |  1992-04-09  |  10KB  |  408 lines

  1. /*
  2.  * Copyright (c) 1992, Brian Berliner and Jeff Polk
  3.  * Copyright (c) 1989-1992, Brian Berliner
  4.  * 
  5.  * You may distribute under the terms of the GNU General Public License as
  6.  * specified in the README file that comes with the CVS 1.3 kit.
  7.  * 
  8.  * Difference
  9.  * 
  10.  * Run diff against versions in the repository.  Options that are specified are
  11.  * passed on directly to "rcsdiff".
  12.  * 
  13.  * Without any file arguments, runs diff against all the currently modified
  14.  * files.
  15.  */
  16.  
  17. #include "cvs.h"
  18.  
  19. #ifndef lint
  20. static char rcsid[] = "@(#)diff.c 1.52 92/04/10";
  21. #endif
  22.  
  23. #if __STDC__
  24. static Dtype diff_dirproc (char *dir, char *pos_repos, char *update_dir);
  25. static int diff_dirleaveproc (char *dir, int err, char *update_dir);
  26. static int diff_file_nodiff (char *file, char *repository, List *entries,
  27.                  List *srcfiles, Vers_TS *vers);
  28. static int diff_fileproc (char *file, char *update_dir, char *repository,
  29.               List * entries, List * srcfiles);
  30. static void diff_mark_errors (int err);
  31. #else
  32. static int diff_fileproc ();
  33. static Dtype diff_dirproc ();
  34. static int diff_dirleaveproc ();
  35. static int diff_file_nodiff ();
  36. static void diff_mark_errors ();
  37. #endif                /* __STDC__ */
  38.  
  39. static char *diff_rev1, *diff_rev2;
  40. static char *diff_date1, *diff_date2;
  41. static char *use_rev1, *use_rev2;
  42. static char *options;
  43. static char opts[PATH_MAX];
  44. static int diff_errors;
  45.  
  46. static char *diff_usage[] =
  47. {
  48.     "Usage: %s %s [-l] [rcsdiff-options]\n",
  49. #ifdef CVS_DIFFDATE
  50.     "    [[-r rev1 | -D date1] [-r rev2 | -D date2]] [files...] \n",
  51. #else
  52.     "    [-r rev1 [-r rev2]] [files...] \n",
  53. #endif
  54.     "\t-l\tLocal directory only, not recursive\n",
  55.     "\t-D d1\tDiff revision for date against working file.\n",
  56.     "\t-D d2\tDiff rev1/date1 against date2.\n",
  57.     "\t-r rev1\tDiff revision for rev1 against working file.\n",
  58.     "\t-r rev2\tDiff rev1/date1 against rev2.\n",
  59.     NULL
  60. };
  61.  
  62. int
  63. diff (argc, argv)
  64.     int argc;
  65.     char *argv[];
  66. {
  67.     char tmp[50];
  68.     int c, err = 0;
  69.     int local = 0;
  70.  
  71.     if (argc == -1)
  72.     usage (diff_usage);
  73.  
  74.     /*
  75.      * Note that we catch all the valid arguments here, so that we can
  76.      * intercept the -r arguments for doing revision diffs; and -l/-R for a
  77.      * non-recursive/recursive diff.
  78.      */
  79.     optind = 1;
  80.     while ((c = gnu_getopt (argc, argv,
  81.            "abcdefhilnpqtuw0123456789BHQRTC:D:F:I:L:V:k:r:")) != -1)
  82.     {
  83.     switch (c)
  84.     {
  85.         case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
  86.         case 'h': case 'i': case 'n': case 'p': case 't': case 'u':
  87.         case 'w': case '0': case '1': case '2': case '3': case '4':
  88.         case '5': case '6': case '7': case '8': case '9': case 'B':
  89.         case 'H': case 'T': case 'Q':
  90.         (void) sprintf (tmp, " -%c", (char) c);
  91.         (void) strcat (opts, tmp);
  92.         if (c == 'Q')
  93.         {
  94.             quiet = 1;
  95.             really_quiet = 1;
  96.             c = 'q';
  97.         }
  98.         break;
  99.         case 'C': case 'F': case 'I': case 'L': case 'V':
  100. #ifndef CVS_DIFFDATE
  101.         case 'D':
  102. #endif
  103.         (void) sprintf (tmp, " -%c%s", (char) c, optarg);
  104.         (void) strcat (opts, tmp);
  105.         break;
  106.         case 'R':
  107.         local = 0;
  108.         break;
  109.         case 'l':
  110.         local = 1;
  111.         break;
  112.         case 'q':
  113.         quiet = 1;
  114.         break;
  115.         case 'k':
  116.         if (options)
  117.             free (options);
  118.         options = RCS_check_kflag (optarg);
  119.         break;
  120.         case 'r':
  121.         if (diff_rev2 != NULL || diff_date2 != NULL)
  122.             error (1, 0,
  123.                "no more than two revisions/dates can be specified");
  124.         if (diff_rev1 != NULL || diff_date1 != NULL)
  125.             diff_rev2 = optarg;
  126.         else
  127.             diff_rev1 = optarg;
  128.         break;
  129. #ifdef CVS_DIFFDATE
  130.         case 'D':
  131.         if (diff_rev2 != NULL || diff_date2 != NULL)
  132.             error (1, 0,
  133.                "no more than two revisions/dates can be specified");
  134.         if (diff_rev1 != NULL || diff_date1 != NULL)
  135.             diff_date2 = Make_Date (optarg);
  136.         else
  137.             diff_date1 = Make_Date (optarg);
  138.         break;
  139. #endif
  140.         case '?':
  141.         default:
  142.         usage (diff_usage);
  143.         break;
  144.     }
  145.     }
  146.     argc -= optind;
  147.     argv += optind;
  148.  
  149.     /* make sure options is non-null */
  150.     if (!options)
  151.     options = xstrdup ("");
  152.  
  153.     /* start the recursion processor */
  154.     err = start_recursion (diff_fileproc, (int (*) ()) NULL, diff_dirproc,
  155.                diff_dirleaveproc, argc, argv, local,
  156.                W_LOCAL, 0, 1, (char *) NULL, 1);
  157.  
  158.     /* clean up */
  159.     free (options);
  160.     return (err);
  161. }
  162.  
  163. /*
  164.  * Do a file diff
  165.  */
  166. /* ARGSUSED */
  167. static int
  168. diff_fileproc (file, update_dir, repository, entries, srcfiles)
  169.     char *file;
  170.     char *update_dir;
  171.     char *repository;
  172.     List *entries;
  173.     List *srcfiles;
  174. {
  175.     int status, err = 2;        /* 2 == trouble, like rcsdiff */
  176.     Vers_TS *vers;
  177.  
  178.     vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL,
  179.                file, 1, 0, entries, srcfiles);
  180.  
  181.     if (vers->vn_user == NULL)
  182.     {
  183.     error (0, 0, "I know nothing about %s", file);
  184.     freevers_ts (&vers);
  185.     diff_mark_errors (err);
  186.     return (err);
  187.     }
  188.     else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
  189.     {
  190.     error (0, 0, "%s is a new entry, no comparison available", file);
  191.     freevers_ts (&vers);
  192.     diff_mark_errors (err);
  193.     return (err);
  194.     }
  195.     else if (vers->vn_user[0] == '-')
  196.     {
  197.     error (0, 0, "%s was removed, no comparison available", file);
  198.     freevers_ts (&vers);
  199.     diff_mark_errors (err);
  200.     return (err);
  201.     }
  202.     else
  203.     {
  204.     if (vers->vn_rcs == NULL && vers->srcfile == NULL)
  205.     {
  206.         error (0, 0, "cannot find revision control file for %s", file);
  207.         freevers_ts (&vers);
  208.         diff_mark_errors (err);
  209.         return (err);
  210.     }
  211.     else
  212.     {
  213.         if (vers->ts_user == NULL)
  214.         {
  215.         error (0, 0, "cannot find %s", file);
  216.         freevers_ts (&vers);
  217.         diff_mark_errors (err);
  218.         return (err);
  219.         }
  220.     }
  221.     }
  222.  
  223.     if (diff_file_nodiff (file, repository, entries, srcfiles, vers))
  224.     {
  225.     freevers_ts (&vers);
  226.     return (0);
  227.     }
  228.  
  229.     (void) fflush (stdout);
  230.     if (use_rev2)
  231.     {
  232.     run_setup ("%s%s %s %s -r%s -r%s", Rcsbin, RCS_DIFF,
  233.            opts, *options ? options : vers->options,
  234.            use_rev1, use_rev2);
  235.     }
  236.     else
  237.     {
  238.     run_setup ("%s%s %s %s -r%s", Rcsbin, RCS_DIFF, opts,
  239.            *options ? options : vers->options, use_rev1);
  240.     }
  241.     run_arg (vers->srcfile->path);
  242.  
  243.     switch ((status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
  244.     RUN_REALLY|RUN_COMBINED)))
  245.     {
  246.     case -1:            /* fork failed */
  247.         error (1, errno, "fork failed during rcsdiff of %s",
  248.            vers->srcfile->path);
  249.     case 0:                /* everything ok */
  250.         err = 0;
  251.         break;
  252.     default:            /* other error */
  253.         err = status;
  254.         break;
  255.     }
  256.  
  257.     (void) fflush (stdout);
  258.     freevers_ts (&vers);
  259.     diff_mark_errors (err);
  260.     return (err);
  261. }
  262.  
  263. /*
  264.  * Remember the exit status for each file.
  265.  */
  266. static void
  267. diff_mark_errors (err)
  268.     int err;
  269. {
  270.     if (err > diff_errors)
  271.     diff_errors = err;
  272. }
  273.  
  274. /*
  275.  * Print a warm fuzzy message when we enter a dir
  276.  */
  277. /* ARGSUSED */
  278. static Dtype
  279. diff_dirproc (dir, pos_repos, update_dir)
  280.     char *dir;
  281.     char *pos_repos;
  282.     char *update_dir;
  283. {
  284.     /* XXX - check for dirs we don't want to process??? */
  285.     if (!quiet)
  286.     error (0, 0, "Diffing %s", update_dir);
  287.     return (R_PROCESS);
  288. }
  289.  
  290. /*
  291.  * Concoct the proper exit status.
  292.  */
  293. /* ARGSUSED */
  294. static int
  295. diff_dirleaveproc (dir, err, update_dir)
  296.     char *dir;
  297.     int err;
  298.     char *update_dir;
  299. {
  300.     return (diff_errors);
  301. }
  302.  
  303. /*
  304.  * verify that a file is different 0=same 1=different
  305.  */
  306. static int
  307. diff_file_nodiff (file, repository, entries, srcfiles, vers)
  308.     char *file;
  309.     char *repository;
  310.     List *entries;
  311.     List *srcfiles;
  312.     Vers_TS *vers;
  313. {
  314.     Vers_TS *xvers;
  315.     char tmp[L_tmpnam+1];
  316.  
  317.     /* free up any old use_rev* variables and reset 'em */
  318.     if (use_rev1)
  319.     free (use_rev1);
  320.     if (use_rev2)
  321.     free (use_rev2);
  322.     use_rev1 = use_rev2 = (char *) NULL;
  323.  
  324.     if (diff_rev1 || diff_date1)
  325.     {
  326.     /* special handling for TAG_HEAD */
  327.     if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0)
  328.         use_rev1 = xstrdup (vers->vn_rcs);
  329.     else
  330.     {
  331.         xvers = Version_TS (repository, (char *) NULL, diff_rev1,
  332.                 diff_date1, file, 1, 0, entries, srcfiles);
  333.         if (xvers->vn_rcs == NULL)
  334.         {
  335.         if (diff_rev1)
  336.             error (0, 0, "tag %s is not in file %s", diff_rev1, file);
  337.         else
  338.             error (0, 0, "no revision for date %s in file %s",
  339.                diff_date1, file);
  340.         return (1);
  341.         }
  342.         use_rev1 = xstrdup (xvers->vn_rcs);
  343.         freevers_ts (&xvers);
  344.     }
  345.     }
  346.     if (diff_rev2 || diff_date2)
  347.     {
  348.     /* special handling for TAG_HEAD */
  349.     if (diff_rev2 && strcmp (diff_rev2, TAG_HEAD) == 0)
  350.         use_rev2 = xstrdup (vers->vn_rcs);
  351.     else
  352.     {
  353.         xvers = Version_TS (repository, (char *) NULL, diff_rev2,
  354.                 diff_date2, file, 1, 0, entries, srcfiles);
  355.         if (xvers->vn_rcs == NULL)
  356.         {
  357.         if (diff_rev1)
  358.             error (0, 0, "tag %s is not in file %s", diff_rev2, file);
  359.         else
  360.             error (0, 0, "no revision for date %s in file %s",
  361.                diff_date2, file);
  362.         return (1);
  363.         }
  364.         use_rev2 = xstrdup (xvers->vn_rcs);
  365.         freevers_ts (&xvers);
  366.     }
  367.  
  368.     /* now, see if we really need to do the diff */
  369.     return (strcmp (use_rev1, use_rev2) == 0);
  370.     }
  371.     if (use_rev1 == NULL || strcmp (use_rev1, vers->vn_user) == 0)
  372.     {
  373.     if (strcmp (vers->ts_rcs, vers->ts_user) == 0 &&
  374.         (!(*options) || strcmp (options, vers->options) == 0))
  375.     {
  376.         return (1);
  377.     }
  378.     if (use_rev1 == NULL)
  379.         use_rev1 = xstrdup (vers->vn_user);
  380.     }
  381.  
  382.     /*
  383.      * with 0 or 1 -r option specified, run a quick diff to see if we
  384.      * should bother with it at all.
  385.      */
  386.     run_setup ("%s%s -p -q %s -r%s", Rcsbin, RCS_CO,
  387.            *options ? options : vers->options, use_rev1);
  388.     run_arg (vers->srcfile->path);
  389.     switch (run_exec (RUN_TTY, tmpnam (tmp), RUN_TTY, RUN_REALLY))
  390.     {
  391.     case 0:                /* everything ok */
  392.         if (xcmp (file, tmp) == 0)
  393.         {
  394.         (void) unlink (tmp);
  395.         return (1);
  396.         }
  397.         break;
  398.     case -1:            /* fork failed */
  399.         (void) unlink (tmp);
  400.         error (1, errno, "fork failed during checkout of %s",
  401.            vers->srcfile->path);
  402.     default:
  403.         break;
  404.     }
  405.     (void) unlink (tmp);
  406.     return (0);
  407. }
  408.